let s:MSG = api#import('message')

let s:heartbeat_option = { "repeat": -1 }
let s:heartbeat_interval = 100
let s:timer = 0

function! api#heartbeat#start() abort
    if s:timer == 0 
        let l:millsecond = get(g:, 'heartbeat_interval', s:heartbeat_interval)
        let l:Callback = get(g:, 'heartbeat_callback', function("api#heartbeat#callback"))
        let l:option = get(g:, 'heartbeat_option', s:heartbeat_option)
        let s:timer = timer_start(l:millsecond, l:Callback, l:option)
    endif
endfunction

function! api#heartbeat#stop() abort
    if s:timer != 0
        call timer_stop(s:timer)
        let s:timer = 0
    endif
endfunction

function! api#heartbeat#get(name) abort
    if has_key(s:, a:name)
        return s:[a:name]
    endif
    return {}
endfunction

let s:waiting_tasks = []
let s:completed_tasks = []

function! api#heartbeat#callback(timer) abort
    if !empty(s:waiting_tasks)
        call s:MSG.debug("Find waiting tasks.")
        let l:task = remove(s:waiting_tasks, 0)
        let l:task = l:task->run()
        call insert(s:completed_tasks, l:task)
    else
        call s:MSG.debug("No waiting tasks found.")
    endif
endfunction

function! api#heartbeat#async(func, ...) abort
    let l:task = deepcopy(s:task)
    if type(a:func) == v:t_string
        let l:task.statement = a:func
    endif
    if type(a:func) == v:t_func
        let l:task.function = a:func
        let l:task.arguments = a:000
    endif
    call s:MSG.debug("task created. " . string(l:task))
    let s:waiting_tasks = insert(s:waiting_tasks, l:task)
    call s:MSG.debug("task inserted into queque " . string(s:waiting_tasks) )
    return s:waiting_tasks[0]
endfunction

function! s:on_data(callback, append=v:true) dict
    if type(a:callback) == v:t_func
        if a:append
            call add(self.handlers.data, a:callback)
        else
            call insert(self.handlers.data, a:callback)
        endif
    endif
endfunction

function! s:on_error(callback, append=v:true) dict
    if type(a:callback) == v:t_func
        if a:append
            call add(self.handlers.error, a:callback)
        else
            call insert(self.handlers.error, a:callback)
        endif
    endif
endfunction

